import os
from math import ceil

from matplotlib import pyplot as plt

from PIL import Image as img
from PIL import ImageFilter as ift


class Filter:
    """
    过滤器基类
    """
    def __init__(self, image: img.Image, *params):
        self._image = image
        self._params = params

    def filter(self):
        pass


class FindEdges(Filter):
    """
    查找边缘
    """
    def filter(self):
        print(type(self._image))
        return self._image.filter(ift.FIND_EDGES)


class Sharpen(Filter):
    """
    锐化
    """
    def filter(self):
        return self._image.filter(ift.SHARPEN)


class Blur(Filter):
    """
    模糊
    """
    def filter(self):
        return self._image.filter(ift.BLUR)


class Resize(Filter):
    """
    调整尺寸
    """
    def filter(self):
        return self._image.resize(self._params[0][0])


class ImageShop:

    def __init__(self, path, img_type=""):
        self._type = img_type   # 图片类型
        self._path = path       # 路径
        self.__p_type = -1      # 输入路径类型
        self._images = []       #
        self._name = []
        self._tmp = []
        self._results = []

    def load_images(self):
        """
        从路径加载特定格式的图片，应加载文件或目录中的所有特定格式图片
        :return:
        """
        if os.path.isfile(self._path):
            i = img.open(self._path)
            self._images.append(i)
            self.__p_type = 0
            self._results = self._images
            self._name.append(os.path.splitext(os.path.split(self._path)[1])[0])
        elif os.path.isdir(self._path):
            file_list = os.listdir(self._path)
            self.__p_type = 1
            for name in file_list:
                if name.endswith(self._type):
                    i = img.open(self._path + "/" + name)
                    self._images.append(i)
                    self._name.append(os.path.splitext(name)[0])
            self._results = self._images
        else:
            print("Invalid path.")

    def __batch_ps(self, Filter, *params):
        """
        利有某个过滤器对所有图片进行处理；
        :param Filter:
        :param params:
        :return:
        """
        i = self._tmp if self._tmp else self._images
        self._results = []
        for image in i:
            f = Filter(image, params) if params else Filter(image)
            self._results.append(f.filter())
        else:
            self.tmp = self._results

    def batch_ps(self, *params):
        """
        批量处理图片的对外公开方法

        注意该方法要至少有一个操作参数，且该参数可以不定长，即可以处理同时进行若干操作（如调整大小并模糊等）

        其参数可定义成一种特定格式的tuple输入，比如（操作名，参数），根据操作名生成对应的Filter子类并调用__batch_ps来完成批处理；
        :param params:
        :return:
        """
        filter_dict = {"FindEdges": FindEdges, "Sharpen": Sharpen, "Blur": Blur, "Resize": Resize}
        for param in params:
            if param[0] == "Resize":
                self.__batch_ps(filter_dict[param[0]], param[1])
            else:
                self.__batch_ps(filter_dict[param[0]])

    def display(self, row: int, col: int, size: tuple, max_num: int = 0):
        """
        处理效果显示（display（）），注意该方法应该有参数，如考虑多图片呈现可能需要行，列以及每张图片的大小，以及最多显示多少张等，可通过matplotlib中的subplot等来实现；
        :param max_num:
        :param row:
        :param col:
        :param size:
        :return:
        """
        if max_num <= 0:
            max_num = len(self._results)
        a = min(row, ceil(max_num / col))
        b = min(max_num, col)
        plt.figure("Results Display")
        r = self._results
        for i in range(max_num):
            plt.subplot(a, b, i + 1)
            plt.imshow(r[i].resize(size))
            plt.axis('off')
            plt.title(self._name[i])
        plt.show()

    def get_results(self):
        return self._results

    def save(self, path: str, img_type: str = "jpg"):
        """
        处理结果输出（save()），该方法应该有输出路径或输出格式等参数。
        :param path:
        :param img_type:
        :return:
        """
        if self.__p_type:
            if not os.path.exists(path):
                os.mkdir(path)
            for i, result in enumerate(self._results):
                result.save(path + f"/{self._name[i]}." + img_type, img_type)
        else:
            dpath = os.path.split(path)
            if not os.path.exists(dpath[0]):
                os.mkdir(dpath[0])
            fname = os.path.splitext(dpath[1])
            if fname[1] != "":
                img_type = fname[1][1:]
            self._results[0].save(dpath[0] + "/" + self._name[0] + "." + img_type, img_type)
